type FieldItem<V, K, A> = {
    [p in keyof A]: A[p];
} & { value: V; name: K };

type TDefineFields = <
    T extends FieldItem<V, K, A>[],
    K extends string | number | symbol = T[number]['name'],
    V extends string | number | symbol = T[number]['value'],
    A = Omit<T[number], 'name' | 'value'>
>(
    fields: T
) => {
    getFieldByName: (name: K) => FieldItem<V, K, A> | null;
    getFieldByValue: (value: V) => FieldItem<V, K, A> | null;
    getFieldByIndex: (index: number) => FieldItem<V, K, A> | null;
    getField: (keyOrName: K | V) => FieldItem<V, K, A> | null;
    getFields: () => T;
};

const defineFields: TDefineFields = (fields) => {
    const fieldNameMapping = new Map(fields.map((field) => [field.name, field]));
    const fieldValueMapping = new Map(fields.map((field) => [field.value, field]));

    return {
        getFieldByName(name) {
            return fieldNameMapping.get(name) ?? null;
        },
        getFieldByValue(value) {
            return fieldValueMapping.get(value) ?? null;
        },
        getFieldByIndex(index: number) {
            return fields[index] ?? null;
        },
        getFields() {
            return fields;
        },
        getField(keyOrName: any) {
            if (fieldNameMapping.has(keyOrName as any)) {
                return fieldNameMapping.get(keyOrName as any) ?? null;
            }
            if (fieldValueMapping.has(keyOrName as any)) {
                return fieldValueMapping.get(keyOrName as any) ?? null;
            }
            return null;
        },
    };
};

export { defineFields };
